/*
 * @Author: zh
 * @Date: 2024-12-06 13:55:49
 * @LastEditors: zh
 * @LastEditTime: 2024-12-10 13:17:26
 * @Description: 22
 */
import colors from 'picocolors';
import { readPackageJSON } from 'pkg-types';
import { type PluginOption } from 'vite';

import { getEnvConfig } from '../utils/env'
import { createContentHash } from "../utils/hash"

const GLOBAL_CONFIG_FILE_NAME = '_app.config.js';
const PLUGIN_NAME = 'app-config';
/**
 * 异步创建应用程序配置插件。
 * 
 * 此函数根据是否处于构建模式创建插件配置对象。在非构建模式下，直接返回插件名称。
 * 在构建模式下，从项目的 package.json 文件中读取版本信息，并根据解析的配置处理配置和源代码。
 * 它还会在构建过程中将配置脚本标签注入到 HTML 中，并生成相应的资产文件。
 * 
 * @param {Object} params - 创建插件的配置参数对象。
 * @param {string} params.root - 项目根目录。
 * @param {boolean} params.isBuild - 是否处于构建模式。
 * @returns {Promise<PluginOption>} 返回一个 Promise，解析为插件配置对象。
 */
async function createAppConfigPlugin({ root, isBuild }: { root: string, isBuild: boolean }): Promise<PluginOption> {

    let publicPath: string;
    let source: string;
    // 如果不是构建模式，直接返回插件名称
    if (!isBuild) {
        return { name: PLUGIN_NAME }
    }
    // 从 package.json 文件中读取版本信息
    const { version = '' } = await readPackageJSON(root);

    return {
        name: PLUGIN_NAME,
        /**
         * 配置解析回调，用于获取解析后的配置并初始化变量。
         * 
         * @param {Object} _config - 解析后的配置对象。
         */
        async configResolved(_config) {
            const appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? ''
            // appTitle = appTitle.replace(/\s/g, '_').replace(/-/g, '_');
            publicPath = _config?.base
            source = await getConfigSource(appTitle);
        },
        /**
         * 转换 index.html 文件，注入配置脚本标签。
         * 
         * @param {string} html - 原始 HTML 内容。
         * @returns {Object} - 包含转换后的 HTML 和注入的标签。
         */
        async transformIndexHtml(html) {
            publicPath = publicPath.endsWith('/') ? publicPath : `${publicPath}/`;

            const appConfigSrc = `${publicPath || '/'}${GLOBAL_CONFIG_FILE_NAME}?V=${version}-${createContentHash(source)}`;

            return {
                html,
                tags: [
                    {
                        tag: 'script',
                        attrs: {
                            src: appConfigSrc
                        },
                    }
                ]
            }
        },
        /**
         * 生成打包文件。
         */
        async generateBundle() {
            try {
                this.emitFile({
                    type: 'asset',
                    fileName: GLOBAL_CONFIG_FILE_NAME,
                    source
                })
                console.log(colors.cyan(`✨配置文件构建成功！`));

            } catch (error) {
                console.log(
                    colors.red('配置文件打包失败:\n' + error),
                );
            }
        }
    }
}

const getVariableName = (title: string) => {
    function strToHex(str: string) {
        const result: string[] = [];
        for (let i = 0; i < str.length; i++) {
            const hex = str.charCodeAt(i).toString(16);
            result.push(('000' + hex).slice(-4))
        }
        return result.join('').toUpperCase();
    }
    return `__PRODUCTION__${strToHex(title) || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
}

async function getConfigSource(appTitle: string) {
    const config = await getEnvConfig()
    const variableName = getVariableName(appTitle)
    const windowVariable = `window.${variableName}`

    let source = `${windowVariable}=${JSON.stringify(config)}`
    source += `
        object.freeze(${variableName});
        object.defineProperty(window, "${variableName}",{
            configurable: false,
            writable: false
        })
    `.replace(/\s/g, '')

    return source
}
export { createAppConfigPlugin }